// ScriptParser.cpp: implementation of the ScriptParser class.
//
//////////////////////////////////////////////////////////////////////

#include "ScriptParser.h"


#include <string.h>
#include <io.h>
#include <fstream>
#include <iostream>
using namespace std;


//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

ScriptParser::ScriptParser()
{
#ifdef SCRIPT_PARSER_DEBUG_DUMP_GENERAL
	cout << "ScriptParser::ScriptParser()" << endl;
#endif

	filename = "";
	
	delimWhiteSpace = "\n\t ";
	delimSeparatorLength =  (int)strlen(delimWhiteSpace);


	delimSeparator = "\n\t ,";
	delimSeparatorLength =  (int)strlen(delimSeparator);
	
	delimString = "\"";
	delimStringLength = (int)strlen(delimString);
	
	token = NULL;
	fileData = NULL;
	fileDataNextIndex = 0;
	fileDataCurrentIndex = -1;
	fileSize = 0;

	lastDelimParsedIndex = -1;
	lastDelimParsed = '\0';
	
}

ScriptParser::~ScriptParser()
{
#ifdef SCRIPT_PARSER_DEBUG_DUMP_GENERAL
	cout << "ScriptParser::~ScriptParser()" << endl;
#endif

	if(fileData)
		delete fileData;
}


bool ScriptParser::Create(char *_filename){

#ifdef SCRIPT_PARSER_DEBUG_DUMP_GENERAL
	cout << "void ScriptParser::Create(char *_filename)" << endl;
#endif

	// get file information
	_finddata_t fileinfo;
	
	if ( _findfirst( _filename, &fileinfo) == -1){
		cout << "====>> Error opening file: [" << filename << "]" << endl;
		return false;
	}

	filename = _filename;
	fileSize = fileinfo.size + 1; // extra byte indicating the end of the file (set to NULL)

#ifdef SCRIPT_PARSER_DEBUG_DUMP_GENERAL
	cout << "size: " << fileinfo.size  << endl;
#endif

	fileData = new char[fileSize];  
	memset(fileData, 0, fileSize);


	ifstream fin(filename);

	char byte;
	int index = 0;

	while (fin.read(&byte, sizeof(byte))){
		//cout <<"reading char: [" << byte<< "]" <<endl;
		fileData[index++] = byte;
	}
	fin.close();

#ifdef SCRIPT_PARSER_DEBUG_DUMP_GENERAL
	cout << "fileData loaded" << endl;
#endif

#ifdef SCRIPT_PARSER_DEBUG_DUMP_FILEDATA
	for (index=0; index<fileSize; index++){
		cout <<"fileData: [" << fileData[index] << "]" <<endl;	
	}
#endif

	return true;
}


bool ScriptParser::SkipCharsInFront(char *chars){
	//cout << "ScriptParser::SkipCharsInFront(char *chars)" <<endl;

	if (fileDataNextIndex >= fileSize){
		//cout << "EOF, abort, ScriptParser::SkipCharsInFront()" << endl;
		return false;
	}


	int charsLength = (int)strlen(chars);
	bool foundChars = true;
	int i;
	int fileDataNewIndex = fileDataNextIndex;

	while(foundChars && (fileDataNewIndex < fileSize)){
		//cout << "checking head" << endl;
		foundChars = false;
		for(i=0; i<charsLength; i++){
			if (fileData[fileDataNewIndex] == chars[i]){
				fileDataNewIndex++;
				foundChars = true;
				break;
			}
		}
	}

	if (fileDataNewIndex >= fileSize){
		//cout << "EOF,  SkipCharsInFront()" << endl;
		fileDataNewIndex = fileSize;  // redundant if checks are >=
	
	}else{ 
		if (fileDataNewIndex != fileDataNextIndex) {
			fileDataNextIndex = fileDataNewIndex;
			return true;
		}
	}

	return false;
}

char* ScriptParser::NextToken(char *delim){
	//cout << "fileDataNextIndex: " << fileDataNextIndex <<endl;

	SkipCharsInFront(delim);

	// start after the last delim
	fileDataCurrentIndex = fileDataNextIndex;

	if(fileDataCurrentIndex >= fileSize){
		//cout << "EOF #" << endl;
		//fileDataCurrentIndex = fileDataNextIndex = fileSize - 1; 
		token = NULL;
		return token;
	}
	
	
	// find where the token ends
	int delimLength = (int)strlen(delim);	
	bool foundDelim = false;
	int i;

	while(!foundDelim && (fileDataNextIndex < fileSize)){
		//cout << "checking for end of token" << endl;
		for(i=0; i<delimLength; i++){
			if (fileData[fileDataNextIndex] == delim[i]){
				foundDelim = true;
				break;
			}
		}
		fileDataNextIndex++;
	}

	if (fileDataNextIndex >= fileSize){
		//cout << "EOF @" << endl;
	}

	// replace delim with NULL
	lastDelimParsed = fileData[fileDataNextIndex - 1];
	lastDelimParsedIndex = fileDataNextIndex - 1;
	fileData[fileDataNextIndex - 1] = '\0';
	
	token = &fileData[fileDataCurrentIndex];

#ifdef SCRIPT_PARSER_DEBUG_SHOW_TOEKN
	if (token){
		cout << "---- ScriptParser::NextToken(), Token: [" << token << "]" << endl;
	}else{
		cout << "---- ScriptParser::NextToken(), Token: NULL !" << endl;
	}
#endif

	return token;
}

void ScriptParser::TokenPush(){

#ifdef SCRIPT_PARSER_DEBUG_SHOW_TOEKN
	cout << "---- ScriptParser::TokenPush(), (push) Token: [" << token << "]" << endl;
#endif
	
	int tokenLength = (int)strlen(token);

	if ((lastDelimParsedIndex < 0) || ((fileDataNextIndex - (tokenLength + 1 /*delim*/)) < 0)){
		//cout << "====> FAILED, ScriptParser::TokenPush()" << endl;
	}else{
		fileData[lastDelimParsedIndex] = lastDelimParsed;
		fileDataNextIndex = lastDelimParsedIndex;
		fileDataNextIndex -= (tokenLength);
	}
}


char* ScriptParser::NextToken(){
	return NextToken(delimSeparator);
}


char* ScriptParser::NextTokenQuotedString(){
	SkipCharsInFront(delimWhiteSpace);
	return NextToken(delimString);
}

long  ScriptParser::GetTokenAsLong(){
	return strtol(token, NULL, 10);
}


// helper function that make a new string and copies token into it. 
// assumes token is not NULL
// NOTE: caller is responsible for freeing the returned copy
char* ScriptParser::TokenCreateCopy(){
	// make a new NULL terminated string
	int len = (int)strlen(token);
	char* copy = new char[len + 1];
	memcpy(copy, token, len + 1);
	return copy;
}


bool  ScriptParser::TokenBeginsWith(char *string)
{
	//cout << "ScriptParser::TokenBeginsWith(char *string)" <<endl;

	if(!token)
		return false;


	int tokenLength = (int)strlen(token);
	int stringLength = (int)strlen(string);
	
	if (tokenLength<stringLength)
		return false;
	
	bool match = true;
	while(match && --stringLength > 0){
		if (string[stringLength] != token[stringLength])
			match = false;
	
	}

#ifdef SCRIPT_PARSER_DEBUG_SHOW_TOEKN
	cout << "---- ScriptParser::TokenBeginsWith(),  Match: " << match << ", Token:[" << token << "], sting: [" << string << "]" << endl;
#endif

	return match;
}
//
void ScriptParser::RemoveComments(char* commentDelim)
{
	char* nextLine = fileData;

	
	while(true)
	{
		char* begLine = strstr(nextLine, commentDelim);

		if(!begLine) break;

		char* endLine = strstr(begLine, "\n");

		if(!endLine)
		{
			endLine = &(fileData[fileSize-1]);
			memset(begLine, ' ',endLine- begLine);
			break;
		}
		memset(begLine, ' ',  endLine- begLine);
		
		nextLine = endLine + 1;
	}


}